#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <iostream>
#include "definitions.hpp"

using namespace std;

void* rdwrif2_thread_func(void* x){

	int sock_eth;

	 if ((sock_eth = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) {
        perror("socket-eth-rdwrif2");
        exit(1);
    }


	/*if ((sock_ip = socket(AF_INET, SOCK_RAW, IPPROTO_IP)) < 0) {
        perror("socket-ip-rdwrif2");
        exit(1);
    }

    if ((ret = setsockopt(sock_ip, IPPROTO_IP, IP_HDRINCL, (char *)&one,
                     sizeof(one))) < 0) {
        perror("setsockopt");
        exit(1);
    }*/


	

	 struct sockaddr from;
     int fromlen;

     memset(&from, '\0', sizeof(from));
     from.sa_family = AF_INET;
    // strcpy(from.sa_data, inter.c_str()); /* or whatever device */
     fromlen = sizeof(from);


	 // ICMP Packet Construction // Begin


struct iphdr* ip;
//struct iphdr* ip_reply;
struct icmphdr* icmp;
struct sockaddr_in connection;
char* packet;
char* buffer;
int sockfd;
int optval;
//int addrlen;
//int siz;



/*
* allocate all necessary memory
*/
packet = (char*)(malloc(sizeof(struct iphdr) + sizeof(struct icmphdr)));
buffer = (char*)(malloc(sizeof(struct iphdr) + sizeof(struct icmphdr)));
/****************************************************************/

ip = (struct iphdr*) packet;
icmp = (struct icmphdr*) (packet + sizeof(struct iphdr));

/*
* here the ip packet is set up
*/
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = sizeof(struct iphdr) + sizeof(struct icmphdr);
ip->id = htons(0);
ip->frag_off = 0;
ip->ttl = 64;
ip->protocol = IPPROTO_ICMP;
//ip->saddr = inet_addr(src_addr);
//ip->daddr = inet_addr(dst_addr);
ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr));

if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
{
perror("socket-af-rdwrif2");
exit(EXIT_FAILURE);
}

/*
* IP_HDRINCL must be set on the socket so that
* the kernel does not attempt to automatically add
* a default ip header to the packet
*/

setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int));

/*
* here the icmp packet is created
* also the ip checksum is generated
*/
icmp->type = ICMP_ECHO;
icmp->code = 0;
icmp->un.echo.id = random();
icmp->un.echo.sequence = 0;
icmp-> checksum = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr));


//connection.sin_family = AF_INET;
//connection.sin_addr.s_addr = inet_addr(dst_addr);


	 // ICMP Packet Construction // End




 while(1){
	 if(packet_q_i1.size() <= 0){
	 usleep(100);
		 continue;
	 }
  unsigned char* buf = packet_q_i1.front();
  packet_q_i1.pop_front();


  // Read the raw message // Begin

   struct sniff_ethernet *eth_hdr;
   struct ip *ip_hdr_in, *ip_hdr_out;

  	eth_hdr     = (struct sniff_ethernet *)buf;
    ip_hdr_in   = (struct ip *)(buf + sizeof(struct ether_header));
    ip_hdr_out   = (struct ip *)buf;

	uint8_t ttl;

	memcpy(&ttl,&ip_hdr_in->ip_ttl,1);

	//string sourceip = string(inet_ntop(ip_hdr_in->ip_dst.s_addr));
	 string sourceip = string(inet_ntoa(ip_hdr_in->ip_dst));

	 if (ip_hdr_in->ip_p == IPPROTO_ICMP && (sourceip.compare("10.10.0.2")==0)){
		 cerr<<"ICMP PROTO ignored";
		 continue;
	 }

	if(ttl == 1){
		//Send ICMP back

		connection.sin_family = AF_INET;
        connection.sin_addr.s_addr = inet_addr(sourceip.c_str());

		ip->saddr = inet_addr("10.10.0.2");
        ip->daddr = inet_addr(sourceip.c_str());
		sendto(sockfd, packet, ip->tot_len, 0, (struct sockaddr *)&connection, sizeof(struct sockaddr));    

		continue;
	}
    ttl = ttl-1;

	memcpy(&ip_hdr_in->ip_ttl,&ttl,1);

    //string sourceip = string(ip_hdr_in->ip_dst.s_addr);
    string destip = string(inet_ntoa(ip_hdr_in->ip_src));

	int lastsip = sourceip.find_last_of(".");
	sourceip = sourceip.substr(0,lastsip);

	int lastdip = destip.find_last_of(".");
	destip = destip.substr(0,lastdip);

    map<string,string>::iterator it1;

	it1 = ipadd_map.find(destip.c_str());
    
	string macadd,parse,inter;

	if(it1!=ipadd_map.end()){
    parse = it1->second;
	int k = parse.find_first_of(",");
	macadd = parse.substr(0,k);
	//cout<<"Before Inter MACADD "<<macadd<<endl;
	//cout<<"Before Inter PARSE "<<parse<<endl;
	//cout<<"Before Inter K "<<k<<endl;
	inter = parse.substr(k+1, (parse.length())-(k+1));
	}else{
		//free(buf);
		continue;
	}


    strcpy(from.sa_data, inter.c_str()); /* or whatever device */


	ip_hdr_out->ip_sum = in_cksum((unsigned short *)buf,ip_hdr_in->ip_hl);

	memcpy(eth_hdr->ether_shost,eth_hdr->ether_dhost,ETHER_ADDR_LEN);
	//eth_hdr->ether_shost = eth_hdr->ether_dhost;
	//eth_hdr->ether_dhost = nametoaddr(macadd.c_str());
    memcpy(eth_hdr->ether_dhost,macadd.c_str(),ETHER_ADDR_LEN);


  // Read the raw message // End


// Write into a raw socket // 

	int r;

     r = sendto(sock_eth, buf, sizeof(buf), 0, &from, fromlen);
	 if(r <0) {
		 perror("sendto-if2");
	 }


 //free(buf);

}// while(1)
}//rdwr function


